package net.guerlab.commons.encrypt;

import java.util.Base64;

import org.apache.commons.codec.digest.DigestUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 混淆加密工具类
 *
 * @author guer
 *
 */
public final class AuthCodeHelper {

    private static final Logger LOGGER = LoggerFactory.getLogger(AuthCodeHelper.class);

    /**
     * 默认随机秘钥长度
     */
    public static final int DEFAULT_KEY_LENGTH = 4;

    private AuthCodeHelper() {
    }

    /**
     * 加密
     *
     * @param str
     *            待加密内容
     * @param key
     *            秘钥
     * @param expiry
     *            有效期，单位毫秒
     * @return 加密结果
     * @throws NullPointerException
     *             当str、key为空的时候抛出NullPointerException异常
     */
    public static String encode(final String str, final String key, final long expiry) {
        return toEncode(str, key, expiry, DEFAULT_KEY_LENGTH);
    }

    /**
     * 加密
     *
     * @param str
     *            待加密内容
     * @param key
     *            秘钥
     * @param expiry
     *            有效期，单位毫秒
     * @param randomKeyLength
     *            随机秘钥长度
     * @return 加密结果
     * @throws NullPointerException
     *             当str、key为空的时候抛出NullPointerException异常
     */
    public static String encode(final String str, final String key, final long expiry, final int randomKeyLength) {
        return toEncode(str, key, expiry, randomKeyLength);
    }

    /**
     * 解密
     *
     * @param str
     *            加密结果
     * @param key
     *            秘钥
     * @return 解密结果
     */
    public static String decode(final String str, final String key) {
        return decode(str, key, DEFAULT_KEY_LENGTH);
    }

    /**
     * 解密
     *
     * @param str
     *            加密结果
     * @param key
     *            秘钥
     * @param randomKeyLength
     *            随机秘钥长度
     * @return 解密结果
     */
    public static String decode(final String str, final String key, final int randomKeyLength) {
        return toDecode(str, key, randomKeyLength);
    }

    /**
     * 解密操作
     *
     * @param str
     *            待加密字符串
     * @param key
     *            加密秘钥
     * @param randomKeyLength
     *            随机秘钥长度
     * @return 解密后字符串
     * @throws NullPointerException
     *             当str、key为空的时候抛出NullPointerException异常
     */
    private static String toDecode(final String str, final String key, final int randomKeyLength) {
        if (str == null) {
            throw new NullPointerException("str cann't to be null");
        }

        long nowTime = System.currentTimeMillis() / 1000;

        AuthCodeKey keys = new AuthCodeKey(key);

        String nowStr = str;

        String keyC = getKeyC(nowStr, randomKeyLength);

        String cryptKey = keys.getKeyA() + DigestUtils.md5Hex(keys.getKeyA() + keyC);

        String result = getResult(nowStr, randomKeyLength, cryptKey);

        long time = getTime(result);

        if (time <= 0 && time < nowTime) {
            return "";
        }

        if (decodeCheck(result, keys)) {
            return result.substring(26);
        }

        result = getResult(nowStr + "=", randomKeyLength, cryptKey);
        if (decodeCheck(result, keys)) {
            return result.substring(26);
        }

        result = getResult(nowStr + "==", randomKeyLength, cryptKey);
        if (decodeCheck(result, keys)) {
            return result.substring(26);
        }

        return "";
    }

    private static String getKeyC(final String nowStr, final int randomKeyLength) {
        String keyC = "";

        if (randomKeyLength != 0) {
            keyC = nowStr.substring(0, randomKeyLength);
        }

        return keyC;
    }

    private static long getTime(String result) {
        try {
            return Long.parseLong(result.substring(0, 10));
        } catch (Exception e) {
            LOGGER.debug(e.getMessage(), e);
        }
        return 0;
    }

    private static String getResult(final String nowStr, final int randomKeyLength, final String cryptKey) {
        byte[] temp = Base64.getDecoder().decode(nowStr.substring(randomKeyLength));

        return new String(RC4Helper.encode(temp, cryptKey));
    }

    private static boolean decodeCheck(String result, AuthCodeKey keys) {
        String aString = DigestUtils.md5Hex(result.substring(26) + keys.getKeyB());

        String data = aString.substring(0, 16);

        return result.substring(10, 26).equals(data);
    }

    /**
     * 加密操作
     *
     * @param str
     *            待加密字符串
     * @param key
     *            加密秘钥
     * @param expiry
     *            有效期
     * @param randomKeyLength
     *            随机秘钥长度
     * @return 加密后字符串
     * @throws NullPointerException
     *             当str、key为空的时候抛出NullPointerException异常
     */
    private static String toEncode(final String str, final String key, final long expiry, final int randomKeyLength) {
        if (str == null) {
            throw new NullPointerException("str cann't to be null");
        }

        long timestamp = System.currentTimeMillis() / 1000;

        AuthCodeKey keys = new AuthCodeKey(key);

        String keyC = "";

        if (randomKeyLength > 0) {
            String data = DigestUtils.md5Hex(String.valueOf(timestamp));
            keyC = data.substring(data.length() - randomKeyLength);
        }

        String cryptKey = keys.getKeyA() + DigestUtils.md5Hex(keys.getKeyA() + keyC);

        String nowStr = str;

        nowStr = String.format("%010d", expiry > 0 ? timestamp + expiry : 0)
                + DigestUtils.md5Hex(nowStr + keys.getKeyB()).substring(0, 16) + nowStr;

        try {
            byte[] temp = RC4Helper.encode(nowStr.getBytes(), cryptKey);

            return keyC + Base64.getEncoder().encodeToString(temp);
        } catch (Exception e) {
            LOGGER.debug(e.getMessage(), e);
        }
        return null;
    }
}